package edu.scnu.fans.fingertip.user.service;

import com.auth0.jwt.JWT;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.JWTVerifier;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import edu.scnu.fans.fingertip.common.api.CommonConstant;
import edu.scnu.fans.fingertip.common.utils.MD5Utils;
import edu.scnu.fans.fingertip.common.web.BusinessException;
import edu.scnu.fans.fingertip.user.input.LoginRegisterInput;
import edu.scnu.fans.fingertip.user.mapper.UserMapper;
import edu.scnu.fans.fingertip.user.model.User;
import edu.scnu.fans.fingertip.user.output.UserOutput;
import edu.scnu.fans.fingertip.user.output.VerifyTokenOutput;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.Date;
import java.util.Optional;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class UserService {

    private static final String KEY = "key-sample";
    private static final String ISSUER = "fans";
    // 过期时间
    private static final long TOKEN_EXP_TIME = 24 * 60 * 60 * 1000;
    private static final String USER_ID = "user_id";



    @Resource
    private UserMapper userMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 根据用户名获取用户
     */
    public UserOutput findUserByUsername(String username) {
        return Optional.ofNullable(userMapper.selectOne(new LambdaQueryWrapper<User>()
                        .eq(User::getUsername, username)))
                .map(User::transferToOutput)
                .orElse(null);
    }

    /**
     * 获取用户信息
     */
    public UserOutput findUserById(Long id) {
        return userMapper.selectById(id).transferToOutput();
    }

    /**
     * 注册
     */
    @Transactional
    public UserOutput register(LoginRegisterInput input) {
        // 检查用户名是否已存在
        if (this.findUserByUsername(input.getUsername()) != null) {
            throw new BusinessException("用户名已存在");
        }

        User user = new User(input.getUsername(), MD5Utils.encryptToMD5(input.getPassword()));
        userMapper.insert(user);

        return user.transferToOutput();
    }


    /**
     * 登陆
     */
    public UserOutput login(LoginRegisterInput input) {
        // 判断用户名密码是否正确
        UserOutput user = Optional.ofNullable(userMapper.selectOne(new LambdaQueryWrapper<User>()
                        .eq(User::getUsername, input.getUsername())
                        .eq(User::getPassword, MD5Utils.encryptToMD5(input.getPassword()))))
                .map(User::transferToOutput)
                .orElseThrow(() -> new BusinessException("用户名或密码错误"));

        // 生成 token
        Date now = new Date();
        Algorithm algorithm = Algorithm.HMAC256(KEY);
        String token = JWT.create()
                .withIssuer(ISSUER)
                .withIssuedAt(now)
                .withExpiresAt(new Date(now.getTime() + TOKEN_EXP_TIME))
                .withClaim(USER_ID, user.getId())
                .sign(algorithm);

        // 存入缓存
        stringRedisTemplate.opsForValue().set(CommonConstant.TOKEN_REDIS_KEY + token, user.getId().toString(),
                TOKEN_EXP_TIME, TimeUnit.MILLISECONDS);

        user.setToken(token);

        return user;
    }

    /**
     * 登出
     */
    public void logout(String token) {
        stringRedisTemplate.delete(CommonConstant.TOKEN_REDIS_KEY + token);
    }

    /**
     * 验证token
     */
    public VerifyTokenOutput verifyToken(Long userId, String token) {
        try {
            Algorithm algorithm = Algorithm.HMAC256(KEY);
            JWTVerifier verifier = JWT.require(algorithm)
                    .withIssuer(ISSUER)
                    .withClaim(USER_ID, userId)
                    .build();

            verifier.verify(token);
            return new VerifyTokenOutput(true);
        } catch (Exception e) {
            log.error("auth failed, userId={}", userId);
            return new VerifyTokenOutput(false);
        }
    }

    // 刷新token机制
}
